﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Media;

namespace FasterWPF
{
    public class BoxingAdorner : Adorner
    {
        public Brush BoxColor { get; set; }
        public FrameworkElement Adornee { get; set; }
        private double _offsetWidth=0.0D;
        private double _offsetHeight=0.0D;
        private double _offsetX = 0.0D;
        private double _offsetY = 0.0D;

        public BoxingAdorner(FrameworkElement adornedElement,
             Brush boxColor)
            : base(adornedElement)
        {
            BoxColor = boxColor;
            Adornee = adornedElement;
        }

        public BoxingAdorner(FrameworkElement adornedElement,
     Brush boxColor, double offsetX, double offsetY, double offsetWidth, double offsetHeight)
            : base(adornedElement)
        {
            BoxColor = boxColor;
            Adornee = adornedElement;
            _offsetX = offsetX;
            _offsetY = offsetY;
            _offsetWidth = offsetWidth;
            _offsetHeight = offsetHeight;
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            drawingContext.DrawRectangle(null, new Pen(BoxColor, 2),
                new Rect(0 + _offsetX, 0 + _offsetY, Adornee.ActualWidth + _offsetWidth, Adornee.ActualHeight + _offsetHeight));
        }
    }

    public class SolidBoxingAdorner : Adorner
    {
        public Brush BoxColor { get; set; }
        public FrameworkElement Adornee { get; set; }
        private double _offsetWidth = 0.0D;
        private double _offsetHeight = 0.0D;
        private double _offsetX = 0.0D;
        private double _offsetY = 0.0D;

        public SolidBoxingAdorner(FrameworkElement adornedElement,
             Brush boxColor)
            : base(adornedElement)
        {
            BoxColor = boxColor;
            Adornee = adornedElement;
        }

        public SolidBoxingAdorner(FrameworkElement adornedElement,
     Brush boxColor, double offsetX, double offsetY, double offsetWidth, double offsetHeight)
            : base(adornedElement)
        {
            BoxColor = boxColor;
            Adornee = adornedElement;
            _offsetX = offsetX;
            _offsetY = offsetY;
            _offsetWidth = offsetWidth;
            _offsetHeight = offsetHeight;
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            drawingContext.DrawRectangle(BoxColor, new Pen(BoxColor, 2),
                new Rect(0 + _offsetX, 0 + _offsetY, Adornee.ActualWidth + _offsetWidth, Adornee.ActualHeight + _offsetHeight));
        }
    }

    public static class AdornerDecoratorExt
    {
        public static void DisplayErrorRectangle(this FrameworkElement targetElement, AdornerLayer al, double offsetX, double offsetY, double offsetWidth, double offsetHeight)
        {
            FasterWPF.BoxingAdorner myAdorner = new FasterWPF.BoxingAdorner(targetElement, Brushes.Red, offsetX, offsetY, offsetWidth, offsetHeight);

            if (!al.ContainsAdornerForElement(targetElement))
            {
                al.Add(myAdorner);
            }
        }

        public static void DisplayErrorRectangle(this FrameworkElement targetElement, AdornerLayer al, double offsetX, double offsetY, double offsetWidth, double offsetHeight, Brush rectangleColor)
        {
            FasterWPF.BoxingAdorner myAdorner = new FasterWPF.BoxingAdorner(targetElement, rectangleColor, offsetX, offsetY, offsetWidth, offsetHeight);

            if (!al.ContainsAdornerForElement(targetElement))
            {
                al.Add(myAdorner);
            }
        }

        public static void RemoveErrorRectangle(this FrameworkElement targetElement, AdornerLayer al)
        {
            if (al.ContainsAdornerForElement(targetElement))
            {
                Adorner elementAdorner = al.GetAdornerOfElement(targetElement);
                al.Remove(elementAdorner);
            }
        }

        public static void DisplayErrorRectangle(this FrameworkElement targetElement)
        {
            AdornerLayer al = AdornerLayer.GetAdornerLayer(targetElement);
            FasterWPF.BoxingAdorner myAdorner = new FasterWPF.BoxingAdorner(targetElement, Brushes.Red);

            if(!al.ContainsAdornerForElement(targetElement))
            {
               al.Add(myAdorner);
            }
        }

        public static void DisplayErrorRectangle(this FrameworkElement targetElement, Brush rectangleColor)
        {
            AdornerLayer al = AdornerLayer.GetAdornerLayer(targetElement);
            FasterWPF.BoxingAdorner myAdorner = new FasterWPF.BoxingAdorner(targetElement, rectangleColor);

            if (!al.ContainsAdornerForElement(targetElement))
            {
                al.Add(myAdorner);
            }
        }

        public static void RemoveErrorRectangle(this FrameworkElement targetElement)
        {
            AdornerLayer al = AdornerLayer.GetAdornerLayer(targetElement);

            if (al.ContainsAdornerForElement(targetElement))
            {
                Adorner elementAdorner = al.GetAdornerOfElement(targetElement);
                al.Remove(elementAdorner);
            }
        }

        #region SolidRectangle

        public static void DisplaySolidRectangle(this FrameworkElement targetElement, AdornerLayer al, double offsetX, double offsetY, double offsetWidth, double offsetHeight)
        {
            FasterWPF.SolidBoxingAdorner myAdorner = new FasterWPF.SolidBoxingAdorner(targetElement, Brushes.Red, offsetX, offsetY, offsetWidth, offsetHeight);

            if (!al.ContainsAdornerForElement(targetElement))
            {
                al.Add(myAdorner);
            }
        }

        public static void DisplaySolidRectangle(this FrameworkElement targetElement, AdornerLayer al, double offsetX, double offsetY, double offsetWidth, double offsetHeight, Brush rectangleColor)
        {
            FasterWPF.SolidBoxingAdorner myAdorner = new FasterWPF.SolidBoxingAdorner(targetElement, rectangleColor, offsetX, offsetY, offsetWidth, offsetHeight);

            if (!al.ContainsAdornerForElement(targetElement))
            {
                al.Add(myAdorner);
            }
        }

        public static void DisplaySolidRectangle(this FrameworkElement targetElement)
        {
            AdornerLayer al = AdornerLayer.GetAdornerLayer(targetElement);
            FasterWPF.SolidBoxingAdorner myAdorner = new FasterWPF.SolidBoxingAdorner(targetElement, Brushes.Red);

            if (!al.ContainsAdornerForElement(targetElement))
            {
                al.Add(myAdorner);
            }
        }

        public static void DisplaySolidRectangle(this FrameworkElement targetElement, Brush rectangleColor)
        {
            AdornerLayer al = AdornerLayer.GetAdornerLayer(targetElement);
            FasterWPF.SolidBoxingAdorner myAdorner = new FasterWPF.SolidBoxingAdorner(targetElement, rectangleColor);

            if (!al.ContainsAdornerForElement(targetElement))
            {
                al.Add(myAdorner);
            }
        }

        #endregion
        
        public static Adorner GetAdornerOfElement(this AdornerLayer al, FrameworkElement targetElement)
        {
            Adorner tempResult = null;

            Adorner[] currentAdorners = al.GetAdorners(targetElement);

            if (currentAdorners != null)
            {
                foreach (Adorner a in currentAdorners)
                {
                    if (a.AdornedElement == targetElement)
                    {
                        tempResult = a;
                    }
                }
            }

            return tempResult;
        }

        public static bool ContainsAdornerForElement(this AdornerLayer al, FrameworkElement targetElement)
        {
            bool tempResult = false;

            Adorner[] currentAdorners = al.GetAdorners(targetElement);

            if (currentAdorners != null)
            {
                foreach (Adorner a in currentAdorners)
                {
                    if (a.AdornedElement == targetElement)
                    {
                        tempResult = true;
                    }
                }
            }

            return tempResult;
        }

        public static AdornerDecorator Initialize(this AdornerDecorator source, ContainerType containerType, Orientation orientation, int? rowSpan, int? colSpan)
        {
            if (rowSpan != null)
            {
                Grid.SetRowSpan(source, (int)rowSpan);
            }

            if (colSpan != null)
            {
                Grid.SetColumnSpan(source, (int)colSpan);
            }

            return Initialize(source, double.NaN, double.NaN, containerType); //Default width and height to Auto by setting to NaN
        }

        public static AdornerDecorator Initialize(this AdornerDecorator source, double width, double height, ContainerType containerType)
        {   
            //Force the Aero theme by default to assure the structure of the Listbox
            Uri uri = new Uri("/PresentationFramework.Aero;component/themes/Aero.NormalColor.xaml", UriKind.Relative);
            source.Resources.MergedDictionaries.Add(Application.LoadComponent(uri) as ResourceDictionary);
            source.Width = width;
            source.Height = height;

            sharedAdornerDecoratorInitializeCode(source, containerType);

            return source;
        }

        private static void sharedAdornerDecoratorInitializeCode(AdornerDecorator source, ContainerType containerType)
        {
            source.SetContainerType(containerType);
            IComposite icomp = CompositeTypeHelper.CreateIComposite<AdornerDecorator>(source);
            source.SetIComposite<AdornerDecorator>(icomp);

            //STORE SETTING BUILD CHAIN AND GET CHAIN?!?  Put these in a Composite implements an interface
            AdornerDecoratorParent gbsr = new AdornerDecoratorParent();
            source.StoreSetting<AdornerDecorator>("IParent", gbsr);

            source.AddItemBorder(Brushes.Gray, new Thickness(0)); //default Border     
            source.StoreSetting<AdornerDecorator>("BorderSettings", new BorderSettings() { Width = double.NaN, Height = double.NaN, BorderBrush = Brushes.Gray, BorderThickness = new Thickness(0, 0, 0, 0), CornerRadius = new CornerRadius(0), Padding = new Thickness(0) });
        }
        
    }//end of class

    //SimpleCircleAdorner from Microsoft http://msdn.microsoft.com/en-us/library/vstudio/ms771344(v=vs.90).aspx
    // Adorners must subclass the abstract base class Adorner.
    public class SimpleCircleAdorner : Adorner
    {
        // Be sure to call the base class constructor.
        public SimpleCircleAdorner(UIElement adornedElement)
            : base(adornedElement)
        {
            // Any constructor implementation...
        }

        // A common way to implement an adorner's rendering behavior is to override the OnRender
        // method, which is called by the layout subsystem as part of a rendering pass.
        protected override void OnRender(DrawingContext drawingContext)
        {
            // Get a rectangle that represents the desired size of the rendered element
            // after the rendering pass.  This will be used to draw at the corners of the 
            // adorned element.
            Rect adornedElementRect = new Rect(this.AdornedElement.DesiredSize);

            // Some arbitrary drawing implements.
            SolidColorBrush renderBrush = new SolidColorBrush(Colors.Green);
            renderBrush.Opacity = 0.2;
            Pen renderPen = new Pen(new SolidColorBrush(Colors.Navy), 1.5);
            double renderRadius = 5.0;

            // Just draw a circle at each corner.
            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopLeft, renderRadius, renderRadius);
            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopRight, renderRadius, renderRadius);
            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.BottomLeft, renderRadius, renderRadius);
            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.BottomRight, renderRadius, renderRadius);
        }
    }
}//end of namespace
